Python'da SQLAlchemy sessiyalarini boshqarish bo'yicha keng qamrovli qo'llanma. Ma'lumotlar yaxlitligi va barqarorligini ta'minlash uchun mustahkam tranzaktsiya usullari ko'rib chiqiladi.
Python SQLAlchemy sessiyalarini boshqarish: Ma'lumotlar yaxlitligini ta'minlash uchun tranzaktsiyalarni boshqarishni o'zlashtirish
SQLAlchemy — bu ma'lumotlar bazalari bilan o'zaro ishlash uchun keng qamrovli vositalar to'plamini taqdim etuvchi kuchli va moslashuvchan Python kutubxonasidir. SQLAlchemy asosida sessiya tushunchasi yotadi, u ma'lumotlar bazasida bajaradigan barcha operatsiyalar uchun vaqtinchalik maydon vazifasini o'taydi. Ma'lumotlar yaxlitligini saqlash va ma'lumotlar bazasining izchil ishlashini ta'minlash uchun, ayniqsa murakkab ilovalarda bir vaqtning o'zida bir nechta so'rovlarni boshqarishda to'g'ri sessiya va tranzaksiya boshqaruvi juda muhimdir.
SQLAlchemy sessiyalarini tushunish
SQLAlchemy sessiyasi — bu ish birligi, ma'lumotlar bazasi bilan suhbat. U ob'ektlarga kiritilgan o'zgarishlarni kuzatib boradi, bu ularni ma'lumotlar bazasiga bitta atomik operatsiya sifatida saqlash imkonini beradi. Uni ma'lumotlarni rasman saqlashdan oldin o'zgartirishlar kiritadigan ish joyi deb o'ylang. Yaxshi boshqarilmagan sessiyasiz, ma'lumotlar nomuvofiqligi va potentsial buzilish xavfi mavjud.
Sessiya yaratish
Ma'lumotlar bazasi bilan o'zaro aloqa qilishni boshlashdan oldin, siz sessiya yaratishingiz kerak. Bu avval SQLAlchemy'ning engine'idan foydalanib ma'lumotlar bazasiga ulanishni o'rnatishni o'z ichiga oladi.
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
# Database connection string
db_url = 'sqlite:///:memory:' # Replace with your database URL (e.g., PostgreSQL, MySQL)
# Create an engine
engine = create_engine(db_url, echo=False) # echo=True to see the generated SQL
# Define a base for declarative models
Base = declarative_base()
# Define a simple model
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
def __repr__(self):
return f"<User(name='{self.name}', email='{self.email}')>"
# Create the table in the database
Base.metadata.create_all(engine)
# Create a session class
Session = sessionmaker(bind=engine)
# Instantiate a session
session = Session()
Ushbu misolda:
- Kerakli SQLAlchemy modullarini import qilamiz.
- Ma'lumotlar bazasi ulanish satrini (`db_url`) belgilaymiz. Ushbu misol soddalik uchun xotirada joylashgan SQLite ma'lumotlar bazasidan foydalanadi, ammo siz uni ma'lumotlar bazasi tizimingizga (masalan, PostgreSQL, MySQL) mos ulanish satri bilan almashtirishingiz kerak. Maxsus format ma'lumotlar bazasi dvigateli va siz foydalanayotgan drayverga qarab farq qiladi. To'g'ri ulanish satri formati uchun SQLAlchemy hujjatlari va ma'lumotlar bazasi provayderi hujjatlariga murojaat qiling.
- `create_engine()` yordamida `engine` yaratamiz. Engine ulanishlar pulini boshqarish va ma'lumotlar bazasi bilan aloqa qilish uchun javobgardir. `echo=True` parametri disk raskadrovka qilish uchun foydali bo'lishi mumkin, chunki u yaratilgan SQL iboralarini konsolga chiqaradi.
- `declarative_base()` yordamida bazaviy sinf (`Base`) ni belgilaymiz. Bu barcha SQLAlchemy modellarimiz uchun bazaviy sinf sifatida ishlatiladi.
- `User` modelini belgilaymiz, uni `users` deb nomlangan ma'lumotlar bazasi jadvaliga xaritalaymiz.
- `Base.metadata.create_all(engine)` yordamida ma'lumotlar bazasida jadval yaratamiz.
- `sessionmaker(bind=engine)` yordamida sessiya sinfini yaratamiz. Bu sessiya sinfini belgilangan engine'dan foydalanishga sozlaydi.
- Nihoyat, `Session()` yordamida sessiyani instansializatsiya qilamiz.
Tranzaktsiyalarni tushunish
Tranzaksiya — bu ma'lumotlar bazasi operatsiyalarining bir mantiqiy ish birligi sifatida ko'riladigan ketma-ketligi. Tranzaktsiyalar ACID xususiyatlariga amal qiladi:
- Atomlik: Tranzaktsiyadagi barcha operatsiyalar to'liq muvaffaqiyatli yakunlanadi yoki to'liq muvaffaqiyatsiz bo'ladi. Agar tranzaktsiyaning biron bir qismi muvaffaqiyatsizlikka uchrasa, butun tranzaksiya bekor qilinadi (rollback).
- Izchillik: Tranzaksiya ma'lumotlar bazasini to'g'ri holatda saqlashi kerak. U hech qanday ma'lumotlar bazasi cheklovlari yoki qoidalarini buzishi mumkin emas.
- Izolyatsiya: Bir vaqtning o'zida bajariluvchi tranzaktsiyalar bir-biridan izolyatsiya qilingan bo'ladi. Bir tranzaksiya tomonidan kiritilgan o'zgarishlar, birinchi tranzaksiya tasdiqlanmaguncha boshqa tranzaktsiyalar uchun ko'rinmaydi.
- Chidamlilik: Tranzaksiya tasdiqlanganidan so'ng, uning o'zgarishlari doimiy bo'ladi va tizim nosozliklaridan keyin ham saqlanib qoladi.
SQLAlchemy tranzaktsiyalarni boshqarish mexanizmlarini taqdim etadi, bu ACID xususiyatlarining saqlanishini ta'minlaydi.
Asosiy tranzaksiya boshqaruvi
Eng keng tarqalgan tranzaksiya operatsiyalari commit (tasdiqlash) va rollback (bekor qilish) dir.
Tranzaktsiyalarni tasdiqlash (Committing Transactions)
Agar tranzaksiya ichidagi barcha operatsiyalar muvaffaqiyatli yakunlansa, siz tranzaksiyani tasdiqlaysiz (commit). Bu o'zgarishlarni ma'lumotlar bazasiga saqlaydi.
try:
# Add a new user
new_user = User(name='Alice Smith', email='alice.smith@example.com')
session.add(new_user)
# Commit the transaction
session.commit()
print("Transaction committed successfully!")
except Exception as e:
# Handle exceptions
print(f"An error occurred: {e}")
session.rollback()
print("Transaction rolled back.")
finally:
session.close()
Ushbu misolda:
- Sessiyaga yangi `User` ob'ektini qo'shamiz.
- O'zgarishlarni ma'lumotlar bazasiga saqlash uchun `session.commit()` ni chaqiramiz.
- Potentsial istisnolarni boshqarish uchun kodni `try...except...finally` blokiga o'raymiz.
- Agar istisno yuzaga kelsa, tranzaksiya davomida kiritilgan har qanday o'zgarishlarni bekor qilish uchun `session.rollback()` ni chaqiramiz.
- Sessiyani chiqarish va ulanishni ulanish puliga qaytarish uchun har doim `finally` blokida `session.close()` ni chaqiramiz. Bu resurslarning sizib chiqishini oldini olish uchun juda muhimdir. Sessiyalarni yopishmaslik ulanishlarning tugashi va ilovaning barqarorsizligiga olib kelishi mumkin.
Tranzaktsiyalarni bekor qilish (Rolling Back Transactions)
Agar tranzaksiya davomida biron bir xato yuzaga kelsa yoki o'zgarishlar saqlanmasligi kerak deb qaror qilsangiz, tranzaksiyani bekor qilasiz (rollback). Bu ma'lumotlar bazasini tranzaksiya boshlanishidan oldingi holatiga qaytaradi.
try:
# Add a user with an invalid email (example to force a rollback)
invalid_user = User(name='Bob Johnson', email='invalid-email')
session.add(invalid_user)
# The commit will fail if the email is not validated on the database level
session.commit()
print("Transaction committed.")
except Exception as e:
print(f"An error occurred: {e}")
session.rollback()
print("Transaction rolled back successfully.")
finally:
session.close()
Ushbu misolda, agar `invalid_user` ni qo'shish istisno keltirsa (masalan, ma'lumotlar bazasi cheklovini buzish tufayli), `session.rollback()` chaqiruvi urinishdagi kiritishni bekor qiladi va ma'lumotlar bazasini o'zgarishsiz qoldiradi.
Kengaytirilgan tranzaksiya boshqaruvi
Tranzaksiya doirasini belgilash uchun `with` iborasidan foydalanish
Tranzaktsiyalarni boshqarishning yanada Python'cha va mustahkam usuli `with` iborasidan foydalanishdir. Bu istisnolar yuzaga kelsa ham, sessiya to'g'ri yopilishini ta'minlaydi.
from contextlib import contextmanager
@contextmanager
def session_scope():
"""Provide a transactional scope around a series of operations."""
session = Session()
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
# Usage:
with session_scope() as session:
new_user = User(name='Charlie Brown', email='charlie.brown@example.com')
session.add(new_user)
# Operations within the 'with' block
# If no exceptions occur, the transaction is committed automatically.
# If an exception occurs, the transaction is rolled back automatically.
print("User added.")
print("Transaction completed (committed or rolled back).")
`session_scope` funksiyasi kontekst menejeridir. `with` blokiga kirganingizda, yangi sessiya yaratiladi. `with` blokidan chiqqaningizda, sessiya yo tasdiqlanadi (agar istisnolar yuzaga kelmasa) yoki bekor qilinadi (agar istisno yuzaga kelsa). Sessiya har doim `finally` blokida yopiladi.
Ichki tranzaktsiyalar (Savepoints)
SQLAlchemy savepointlar yordamida ichki tranzaktsiyalarni qo'llab-quvvatlaydi. Savepoint butun tranzaksiyaga ta'sir qilmasdan, kattaroq tranzaksiya ichidagi ma'lum bir nuqtaga qaytarish imkonini beradi.
try:
with session_scope() as session:
user1 = User(name='David Lee', email='david.lee@example.com')
session.add(user1)
session.flush() # Send changes to the database but don't commit yet
# Create a savepoint
savepoint = session.begin_nested()
try:
user2 = User(name='Eve Wilson', email='eve.wilson@example.com')
session.add(user2)
session.flush()
# Simulate an error
raise ValueError("Simulated error during nested transaction")
except Exception as e:
print(f"Nested transaction error: {e}")
savepoint.rollback()
print("Nested transaction rolled back to savepoint.")
# Continue with the outer transaction, user1 will still be added
user3 = User(name='Frank Miller', email='frank.miller@example.com')
session.add(user3)
except Exception as e:
print(f"Outer transaction error: {e}")
#Commit will commit user1 and user3, but not user2 due to the nested rollback
try:
with session_scope() as session:
#Verify only user1 and user3 exist
users = session.query(User).all()
for user in users:
print(user)
except Exception as e:
print(f"Unexpected Exception: {e}") #Should not happen
Ushbu misolda:
- `session_scope()` yordamida tashqi tranzaksiyani boshlaymiz.
- `user1` ni sessiyaga qo'shamiz va o'zgarishlarni ma'lumotlar bazasiga yuboramiz (flush). `flush()` o'zgarishlarni ma'lumotlar bazasi serveriga yuboradi, lekin ularni tasdiqlamaydi. Bu butun tranzaksiyani tasdiqlashdan oldin o'zgarishlar to'g'ri (masalan, cheklov buzilishlari yo'q) ekanligini ko'rish imkonini beradi.
- `session.begin_nested()` yordamida savepoint yaratamiz.
- Ichki tranzaksiya ichida `user2` ni qo'shamiz va xatoni simulyatsiya qilamiz.
- `savepoint.rollback()` yordamida ichki tranzaksiyani savepointga qaytaramiz. Bu faqat ichki tranzaksiya ichida qilingan o'zgarishlarni bekor qiladi (ya'ni, `user2` ning qo'shilishini).
- Tashqi tranzaksiyani davom ettiramiz va `user3` ni qo'shamiz.
- Tashqi tranzaksiya tasdiqlanadi, `user1` va `user3` ma'lumotlar bazasiga saqlanadi, `user2` esa savepointning bekor qilinishi tufayli tashlab yuboriladi.
Izolyatsiya darajalarini nazorat qilish
Izolyatsiya darajalari bir vaqtning o'zida bajariluvchi tranzaktsiyalarning bir-biridan qanchalik ajratilganligini belgilaydi. Yuqori izolyatsiya darajalari ma'lumotlarning yuqori izchilligini ta'minlaydi, ammo bir vaqtda bajarish imkoniyatini va ish faoliyatini kamaytirishi mumkin. SQLAlchemy tranzaktsiyalaringizning izolyatsiya darajasini nazorat qilish imkonini beradi.
Umumiy izolyatsiya darajalari quyidagilarni o'z ichiga oladi:
- Read Uncommitted: Eng past izolyatsiya darajasi. Tranzaktsiyalar boshqa tranzaktsiyalar tomonidan tasdiqlanmagan o'zgarishlarni ko'rishi mumkin. Bu iflos o'qishlarga olib kelishi mumkin.
- Read Committed: Tranzaktsiyalar faqat boshqa tranzaktsiyalar tomonidan tasdiqlangan o'zgarishlarni ko'rishi mumkin. Bu iflos o'qishlarning oldini oladi, ammo takrorlanmaydigan o'qishlar va fantoma o'qishlarga olib kelishi mumkin.
- Repeatable Read: Tranzaktsiyalar butun tranzaksiya davomida bir xil ma'lumotlarni ko'rishi mumkin, hatto boshqa tranzaktsiyalar uni o'zgartirsa ham. Bu iflos o'qishlarning va takrorlanmaydigan o'qishlarning oldini oladi, ammo fantoma o'qishlarga olib kelishi mumkin.
- Serializable: Eng yuqori izolyatsiya darajasi. Tranzaktsiyalar bir-biridan to'liq izolyatsiya qilingan. Bu iflos o'qishlarning, takrorlanmaydigan o'qishlarning va fantoma o'qishlarning oldini oladi, ammo bir vaqtda bajarish imkoniyatini sezilarli darajada kamaytirishi mumkin.
Standart izolyatsiya darajasi ma'lumotlar bazasi tizimiga bog'liq. Siz engine yaratishda yoki tranzaksiyani boshlashda izolyatsiya darajasini belgilashingiz mumkin.
Misol (PostgreSQL):
from sqlalchemy.dialects.postgresql import dialect
# Set isolation level when creating the engine
engine = create_engine('postgresql://user:password@host:port/database',
connect_args={'options': '-c statement_timeout=1000'} #Example of timeout
)
# Set the isolation level when beginning a transaction (database specific)
# For postgresql, it's recommended to set it on the connection, not engine.
from sqlalchemy import event
from sqlalchemy.pool import Pool
@event.listens_for(Pool, "connect")
def set_isolation_level(dbapi_connection, connection_record):
existing_autocommit = dbapi_connection.autocommit
dbapi_connection.autocommit = True
cursor = dbapi_connection.cursor()
cursor.execute("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE")
dbapi_connection.autocommit = existing_autocommit
cursor.close()
# Then transactions created via SQLAlchemy will use the configured isolation level.
Muhim: Izolyatsiya darajalarini o'rnatish usuli ma'lumotlar bazasiga xosdir. To'g'ri sintaksis uchun ma'lumotlar bazasi hujjatlariga murojaat qiling. Izolyatsiya darajalarini noto'g'ri o'rnatish kutilmagan xatti-harakatlarga yoki xatolarga olib kelishi mumkin.
Konkurentlikni boshqarish
Bir nechta foydalanuvchilar yoki jarayonlar bir vaqtning o'zida bir xil ma'lumotlarga kirishganda, ma'lumotlar buzilishining oldini olish va ma'lumotlar izchilligini ta'minlash uchun konkurentlikni to'g'ri boshqarish juda muhimdir. SQLAlchemy konkurentlikni boshqarish uchun bir nechta mexanizmlarni taqdim etadi, jumladan optimist qulflash (optimistic locking) va pessimist qulflash (pessimistic locking).
Optimist qulflash
Optimist qulflash ziddiyatlar kam uchraydi deb taxmin qiladi. U tranzaksiyani tasdiqlashdan oldin boshqa tranzaktsiyalar tomonidan kiritilgan o'zgarishlarni tekshiradi. Agar ziddiyat aniqlansa, tranzaksiya bekor qilinadi.
Optimist qulflashni amalga oshirish uchun, odatda, jadvalingizga versiya ustunini qo'shasiz. Ushbu ustun qator yangilanganda avtomatik ravishda oshiriladi.
from sqlalchemy import Column, Integer, String, Integer
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class Article(Base):
__tablename__ = 'articles'
id = Column(Integer, primary_key=True)
title = Column(String)
content = Column(String)
version = Column(Integer, nullable=False, default=1)
def __repr__(self):
return f"<Article(title='{self.title}', version='{self.version}')>"
#Inside of the try catch block
def update_article(session, article_id, new_content):
article = session.query(Article).filter_by(id=article_id).first()
if article is None:
raise ValueError("Article not found")
original_version = article.version
# Update the content and increment the version
article.content = new_content
article.version += 1
# Attempt to update, checking the version column in the WHERE clause
rows_affected = session.query(Article).filter(
Article.id == article_id,
Article.version == original_version
).update({
Article.content: new_content,
Article.version: article.version
}, synchronize_session=False)
if rows_affected == 0:
session.rollback()
raise ValueError("Conflict: Article has been updated by another transaction.")
session.commit()
Ushbu misolda:
- `Article` modeliga `version` ustunini qo'shamiz.
- Maqolani yangilashdan oldin, joriy versiya raqamini saqlaymiz.
- `UPDATE` iborasida versiya ustunining saqlangan versiya raqamiga tengligini tekshiradigan `WHERE` bandini kiritamiz. `synchronize_session=False` SQLAlchemy'ning yangilangan ob'ektni qayta yuklashiga to'sqinlik qiladi; biz versiyalashni aniq boshqaryapmiz.
- Agar versiya ustuni boshqa tranzaksiya tomonidan o'zgartirilgan bo'lsa, `UPDATE` iborasi hech qanday qatorlarga ta'sir qilmaydi (rows_affected 0 bo'ladi) va biz istisno keltiramiz.
- Tranzaksiyani bekor qilamiz va foydalanuvchiga ziddiyat yuzaga kelgani haqida xabar beramiz.
Pessimist qulflash
Pessimist qulflash ziddiyatlar ehtimoli yuqori deb taxmin qiladi. U qatorni yoki jadvalni o'zgartirishdan oldin unga qulf qo'yadi. Bu boshqa tranzaktsiyalarning qulf ochilmaguncha ma'lumotlarni o'zgartirishiga to'sqinlik qiladi.
SQLAlchemy qulflarni olish uchun bir nechta funksiyalarni taqdim etadi, masalan, `with_for_update()`.
# Example using PostgreSQL
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base
# Database setup (replace with your actual database URL)
db_url = 'postgresql://user:password@host:port/database'
engine = create_engine(db_url, echo=False) #Set echo to true if you would like to see the SQL generated
Base = declarative_base()
class Item(Base):
__tablename__ = 'items'
id = Column(Integer, primary_key=True)
name = Column(String)
value = Column(Integer)
def __repr__(self):
return f"<Item(name='{self.name}', value='{self.value}')>"
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
#Function to update the item (within a try/except)
def update_item_value(session, item_id, new_value):
# Acquire a pessimistic lock on the item
item = session.query(Item).filter(Item.id == item_id).with_for_update().first()
if item is None:
raise ValueError("Item not found")
# Update the item's value
item.value = new_value
session.commit()
return True
Ushbu misolda:
- `with_for_update()` dan `Item` qatorini yangilashdan oldin unga qulf qo'yish uchun foydalanamiz. Bu boshqa tranzaktsiyalarning joriy tranzaksiya tasdiqlanmaguncha yoki bekor qilinmaguncha qatorni o'zgartirishiga to'sqinlik qiladi. `with_for_update()` funksiyasi ma'lumotlar bazasiga xosdir; tafsilotlar uchun ma'lumotlar bazasi hujjatlariga murojaat qiling. Ba'zi ma'lumotlar bazalarida turli xil qulflash mexanizmlari yoki sintaksisi bo'lishi mumkin.
Muhim: Pessimist qulflash bir vaqtda bajarish imkoniyatini va ish faoliyatini kamaytirishi mumkin, shuning uchun uni faqat zarur bo'lganda ishlating.
Istisnolarni boshqarish bo'yicha eng yaxshi amaliyotlar
To'g'ri istisnolarni boshqarish ma'lumotlar yaxlitligini ta'minlash va ilovaning ishdan chiqishining oldini olish uchun juda muhimdir. Ma'lumotlar bazasi operatsiyalaringizni har doim `try...except` bloklariga o'rang va istisnolarni to'g'ri boshqaring.
Istisnolarni boshqarish bo'yicha ba'zi eng yaxshi amaliyotlar:
- Maxsus istisnolarni ushlash: `Exception` kabi umumiy istisnolarni ushlashdan saqlaning. Har xil turdagi xatolarni turlicha boshqarish uchun `sqlalchemy.exc.IntegrityError` yoki `sqlalchemy.exc.OperationalError` kabi maxsus istisnolarni ushlang.
- Tranzaktsiyalarni bekor qilish: Agar istisno yuzaga kelsa, har doim tranzaksiyani bekor qiling (rollback).
- Istisnolarni jurnalga yozish: Muammolarni aniqlash va tuzatishga yordam berish uchun istisnolarni jurnalga yozing. Jurnallaringizga iloji boricha ko'proq kontekstni kiriting (masalan, foydalanuvchi IDsi, kiritilgan ma'lumotlar, vaqt tamg'asi).
- Zarur bo'lganda istisnolarni qayta yuzaga keltirish: Agar istisnoni boshqara olmasangiz, uni yuqori darajadagi boshqaruvchining u bilan shug'ullanishiga imkon berish uchun qayta yuzaga keltiring.
- Resurslarni tozalash: Har doim sessiyani yoping va boshqa resurslarni `finally` blokida chiqaring.
import logging
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy.exc import IntegrityError, OperationalError
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Database setup (replace with your actual database URL)
db_url = 'postgresql://user:password@host:port/database'
engine = create_engine(db_url, echo=False)
Base = declarative_base()
class Product(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
name = Column(String)
price = Column(Integer)
def __repr__(self):
return f"<Product(name='{self.name}', price='{self.price}')>"
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
# Function to add a product
def add_product(session, name, price):
try:
new_product = Product(name=name, price=price)
session.add(new_product)
session.commit()
logging.info(f"Product '{name}' added successfully.")
return True
except IntegrityError as e:
session.rollback()
logging.error(f"IntegrityError: {e}")
#Handle database constraint violations (e.g., duplicate name)
return False
except OperationalError as e:
session.rollback()
logging.error(f"OperationalError: {e}")
#Handle connection errors or other operational issues
return False
except Exception as e:
session.rollback()
logging.exception(f"An unexpected error occurred: {e}")
# Handle any other unexpected errors
return False
finally:
session.close()
Ushbu misolda:
- Jarayon davomida voqealarni yozib olish uchun logging'ni sozlaymiz.
- Biz `IntegrityError` (cheklov buzilishlari uchun) va `OperationalError` (ulanish xatolari uchun) kabi maxsus istisnolarni ushlaymiz.
- `except` bloklarida tranzaksiyani bekor qilamiz.
- `logging` moduli yordamida istisnolarni jurnalga yozamiz. `logging.exception()` usuli stack trace'ni avtomatik ravishda jurnal xabariga kiritadi.
- Agar istisnoni boshqara olmasak, uni qayta yuzaga keltiramiz.
- `finally` blokida sessiyani yopamiz.
Ma'lumotlar bazasi ulanishini pooling qilish
SQLAlchemy ma'lumotlar bazasi ulanishlarini samarali boshqarish uchun ulanish havzasidan (connection pooling) foydalanadi. Ulanish havzasi ma'lumotlar bazasiga ochiq ulanishlar to'plamini saqlaydi, bu ilovalarga har bir so'rov uchun yangi ulanishlar yaratish o'rniga mavjud ulanishlarni qayta ishlatish imkonini beradi. Bu ish faoliyatini sezilarli darajada yaxshilashi mumkin, ayniqsa ko'p sonli bir vaqtning o'zida so'rovlarni boshqaradigan ilovalarda.
SQLAlchemy'ning `create_engine()` funksiyasi avtomatik ravishda ulanish havzasini yaratadi. Ulanish havzasini `create_engine()` ga argumentlar o'tkazish orqali sozlash mumkin.
Umumiy ulanish havzasi parametrlari quyidagilarni o'z ichiga oladi:
- pool_size: Havzadagi maksimal ulanishlar soni.
- max_overflow: `pool_size` dan tashqari yaratilishi mumkin bo'lgan ulanishlar soni.
- pool_recycle: Ulanish qayta ishlatiladigan soniyalar.
- pool_timeout: Ulanish mavjud bo'lishini kutish uchun soniyalar.
engine = create_engine('postgresql://user:password@host:port/database',
pool_size=5, #Maximum pool size
max_overflow=10, #Maximum overflow
pool_recycle=3600, #Recycle connections after 1 hour
pool_timeout=30
)
Muhim: Ilovangiz ehtiyojlari va ma'lumotlar bazasi serveringiz imkoniyatlariga asoslanib, tegishli ulanish havzasi sozlamalarini tanlang. Noto'g'ri sozlamalar ishlash muammolariga yoki ulanishlarning tugashiga olib kelishi mumkin.
Asinxron tranzaktsiyalar (Async SQLAlchemy)
Yuqori konkurentlikni talab qiluvchi zamonaviy ilovalar uchun, ayniqsa FastAPI yoki AsyncIO kabi asinxron freymvorklar bilan qurilganlar uchun, SQLAlchemy Async SQLAlchemy deb nomlangan asinxron versiyani taklif etadi.
Async SQLAlchemy asosiy SQLAlchemy komponentlarining asinxron versiyalarini taqdim etadi, bu sizga voqea siklini bloklamasdan ma'lumotlar bazasi operatsiyalarini bajarish imkonini beradi. Bu ilovalaringizning ish faoliyatini va masshtabini sezilarli darajada yaxshilashi mumkin.
Async SQLAlchemy dan foydalanishning asosiy misoli:
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, String
import asyncio
# Database setup (replace with your actual database URL)
db_url = 'postgresql+asyncpg://user:password@host:port/database'
engine = create_async_engine(db_url, echo=False)
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
def __repr__(self):
return f"<User(name='{self.name}', email='{self.email}')>"
async def create_db_and_tables():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async def add_user(name, email):
async with AsyncSession(engine) as session:
new_user = User(name=name, email=email)
session.add(new_user)
await session.commit()
async def main():
await create_db_and_tables()
await add_user("Async User", "async.user@example.com")
if __name__ == "__main__":
asyncio.run(main())
Sinxron SQLAlchemy dan asosiy farqlar:
- `create_engine` o'rniga `create_async_engine` ishlatiladi.
- `Session` o'rniga `AsyncSession` ishlatiladi.
- Barcha ma'lumotlar bazasi operatsiyalari asinxron bo'lib, `await` yordamida kutilishi kerak.
- Asinxron ma'lumotlar bazasi drayverlari (masalan, PostgreSQL uchun `asyncpg`) ishlatilishi kerak.
Muhim: Async SQLAlchemy asinxron operatsiyalarni qo'llab-quvvatlaydigan ma'lumotlar bazasi drayverini talab qiladi. To'g'ri drayver o'rnatilganligiga va sozlangiligiga ishonch hosil qiling.
Xulosa
SQLAlchemy sessiya va tranzaksiya boshqaruvini o'zlashtirish ma'lumotlar bazalari bilan o'zaro ishlaydigan mustahkam va ishonchli Python ilovalarini yaratish uchun juda muhimdir. Sessiyalar, tranzaktsiyalar, izolyatsiya darajalari va konkurentlik tushunchalarini tushunish, shuningdek, istisnolarni boshqarish va ulanish havzasidan foydalanish bo'yicha eng yaxshi amaliyotlarga rioya qilish orqali siz ma'lumotlar yaxlitligini ta'minlashingiz va ilovalaringizning ish faoliyatini optimallashtirishingiz mumkin.
Kichik veb-ilova yoki keng ko'lamli korporativ tizim yaratayotgan bo'lsangiz ham, SQLAlchemy ma'lumotlar bazasi bilan o'zaro munosabatlarni samarali boshqarish uchun kerakli vositalarni taqdim etadi. Ilovalaringizning ishonchliligini ta'minlash uchun har doim ma'lumotlar yaxlitligini ustuvor qiling va potentsial xatolarni nazorat qiling.
Quyidagi ilg'or mavzularni o'rganishni ko'rib chiqing:
- Ikki fazali commit (2PC): Bir nechta ma'lumotlar bazalarini qamrab oluvchi tranzaktsiyalar uchun.
- Sharding: Ma'lumotlarni bir nechta ma'lumotlar bazasi serverlariga tarqatish uchun.
- Ma'lumotlar bazasi migratsiyalari: Ma'lumotlar bazasi sxemasidagi o'zgarishlarni boshqarish uchun Alembic kabi vositalardan foydalanish.